﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using Ue.Framework.Collections;
using Ue.Framework.Reflection;
using Ue.Infrastructure;
using Ue.Infrastructure.DomainBase;
using Ue.Infrastructure.RepositoryFramework;
using Ue.Infrastructure.RepositoryFramework.Repository;
using Ue.Qcdz.Domain.TreeViews;

namespace Ue.Qcdz.Repositories.TreeViews
{
    public class TreeNodeRepository : SqlServerRepository<TreeNode>
    {
        public TreeNodeRepository()
            : base(null) { }

        public TreeNodeRepository(IUnitOfWork uniteOfWork)
            : base(uniteOfWork) { }


        protected override string GetDataTableName()
        {
            return TreeNodeFactory.TableName;
        }

        protected override string[] GetBaseInsertFields()
        {
            return new string[]{
                TreeNodeFactory.FieldNames.EntityKey,
                TreeNodeFactory.FieldNames.TreeViewInNodes,
                TreeNodeFactory.FieldNames.TagInNodes,
                TreeNodeFactory.FieldNames.TagFullType,
                TreeNodeFactory.FieldNames.ParentId,
                TreeNodeFactory.FieldNames.DisplayOrder,
                TreeNodeFactory.FieldNames.CreateOn
            };
        }

        protected override string[] GetBaseUpdateFields()
        {
            return new string[]{
                TreeNodeFactory.FieldNames.ParentId,
                TreeNodeFactory.FieldNames.DisplayOrder
            };
        }

        protected override IDataParameter[] BuildParameters(TreeNode entity)
        {
            return new IDataParameter[]{
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.EntityKey,entity.Key),
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.TreeViewInNodes,entity.TreeView.Key),
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.TagInNodes,entity.Tag==null?null:entity.Tag.Key),
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.TagFullType,this.BuildEnttiyFullTypeString(entity.Tag)),
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.ParentId,entity.Parent==null?null:entity.Parent.Key),
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.DisplayOrder,entity.DisplayOrder),
                this.RepositoryDriver.BuildParameter(TreeNodeFactory.FieldNames.CreateOn,entity.CreateOn)
            };
        }

        protected override IWhereClause BuildWhereClauseBy(IDataQuery dataQuery)
        {
            if(dataQuery is ITreeNodeDataQuery)
            {
                return new TreeNodeWhereClause(dataQuery as ITreeNodeDataQuery);
            }

            return null;
        }

        protected override IDictionary<string, SqlServerRepositoryBase<TreeNode>.ChildDataHandler> GetBuildEntityChildCallbacks()
        {
            var callBacks = new Dictionary<string, SqlServerRepositoryBase<TreeNode>.ChildDataHandler>();

            callBacks.Add(TreeNodeFactory.FieldNames.TagInNodes, delegate(TreeNode node, object fieldValue)
            {
                TreeNodeProxy nodeProxy=node as TreeNodeProxy;
                if (nodeProxy != null
                    && fieldValue != null
                    && nodeProxy.TagType != null)
                {
                    nodeProxy.Tag = BuildTag(nodeProxy.TagType, fieldValue);
                }
            });

            return callBacks;
        }

        public override IDataResult<TreeNode> FindBy(IDataQuery dataQuery)
        {
            IDataResult<TreeNode> nodes = base.FindBy(dataQuery);

            ITreeNodeDataQuery nodeDataQuery = dataQuery as ITreeNodeDataQuery;
            if (nodeDataQuery != null)
            {
                nodes.DataObjects.Foreach(item =>
                {
                    TreeNodeProxy nodeProxy=item as TreeNodeProxy;
                    if (nodeProxy != null)
                    {
                        nodeProxy.TreeView = nodeDataQuery.TreeView;
                    }
                });
            }
            
            return nodes;
        }

        protected override void PersistDeletedItem(TreeNode item)
        {
            item.ChildNodes.Foreach(node =>
            {
                this.PersistDeletedItem(node);
            });

            base.PersistDeletedItem(item);
        }

        protected override void PersistNewItem(TreeNode item)
        {
            base.PersistNewItem(item);

            item.ChildNodes.Foreach(node =>
            {
                this.PersistNewItem(node);
            });
        }


        private ITreeNodeTag BuildTag(Type entityTagType,object tagId)
        {
            Type repositoryInterfaceType = typeof(IRepository<>);
            repositoryInterfaceType=repositoryInterfaceType.MakeGenericType(entityTagType);

            Type type = typeof(RepositoryFactory);
            MethodInfo method = type.GetMethod("GetRepository", new Type[] { });
            method = method.MakeGenericMethod(repositoryInterfaceType, entityTagType);

            if (method != null)
            {
                object repository = method.DynamicMethodExecutor(new RepositoryFactory());

                return repository.DynamicMethodExecutor(
                    itemType => itemType.GetMethod("FindBy", new Type[] { typeof(object) }),
                    tagId) as ITreeNodeTag;
            }

            return null;
        }

        private string BuildEnttiyFullTypeString(ITreeNodeTag tag)
        {
            Type type = tag.GetType();

            string fullTypeName = type.AssemblyQualifiedName;

            int lastIndex = fullTypeName.IndexOf(", Version=");

            return fullTypeName.Substring(0, lastIndex);
        }

        //private int GetCount(TreeView treeView,TreeNode parentNode)
        //{
        //    TreeNodeWhereClause whereClause = new TreeNodeWhereClause(treeView, parentNode);

        //    var query = string.Format(
        //        "select count(*) from {0} where {1}", 
        //        TreeNodeFactory.TableName, 
        //        whereClause.Clause.Replace("where", string.Empty));

        //    return this.RepositoryDriver.ExecuteScalar<int>(query, whereClause.Parameters);
        //}
    }
}
